<?php

class Tyh_Hook
{
	protected $sender;

	/*
	 * Contains all registered filters and actions added by class is tyh-mvc.
	 */
	protected static $hooks = array();
	
	protected $hook_type;


	protected function __construct( &$sender )
	{
		$this->sender = $sender;
	}
	
	public static function create( $sender, $class = '' )
	{
		return new $class($sender); 
	}
	
	public function &add( $tag, $callback = '', $priority = 10 , $accepted_args = 1, $type = 'action' )
	{		
		$add = 'add_' . $type;
		
		// TODO: add more 'default' WP return functions
		switch($callback)
		{
			case '__return_false':
				$add($tag, $callback, $priority, $accepted_args);
				return $this;
		}
		
		$callback = ( empty( $callback ) ) ? $tag : $callback;
		
		$add($tag, array(&$this->sender, $callback), $priority, $accepted_args);
		
		$sender_classname = get_class($this->sender);
		
		self::$hooks[$type . 's'][$tag][$sender_classname][] = array(
			'callback' => $callback,
			'priority' => $priority,
			'accepted_args' => $accepted_args
		);

		return $this;
	}
	
	public function remove( $tag, $function_to_remove = '', $priority = 10 , $accepted_args = 1)
	{	
		// if no function was given, $tag was used as callback
		$function_to_remove = (empty( $function_to_remove )) ? $tag : $function_to_remove;
		
		// remove action
		if( $this->hook_type === 'action')
		{
			$hook_removed = remove_action( $tag, array( &$this->sender, $function_to_remove ), $priority, $accepted_args );
		}
		else if($this->hook_type === 'filter')
		{
			$hook_removed = remove_filter( $tag, array( &$this->sender, $function_to_remove ), $priority, $accepted_args );
		}
		
		$hooks =& self::$hooks;
		$type = $this->hook_type . 's';
		
		// check if tag exists in hooks
		if ( ! array_key_exists($tag, $hooks[$type]))
		{
			$tyh = tyh_get_instance();
			
			if( $tyh->is_debug_on() )
			{
				trigger_error( _x( 'Action is not set. Action: "', TYH_LANG_CONTEXT ) . $tag . '"' );
			}
			
			return FALSE;
		}
		
		// get the name of the class who is requesting to remove a hook
		$sender_class = get_class($this->sender);
		
		// check if the sender class is properly registered
		if( !isset( $hooks[ $type ][ $tag ][ $sender_class ] ) ) 
			return FALSE;
		
		$obj_callbacks =& $hooks[$type][$tag][$sender_class];
		
		// remove from hooks
		foreach($obj_callbacks as $index => &$key)
		{		
			if( $key['callback'] === $function_to_remove)
			{
				unset($obj_callbacks[$index]);
			}
		}
		
		// removes empty sender object 
		if( empty($hooks[$type][$tag][$sender_class]))
		{
			unset($hooks[$type][$tag][$sender_class]);
		}
		// removes empty tag array
		if( empty($hooks[$type][$tag]))
		{
			unset($hooks[$type][$tag]);
		}
		
		return $hook_removed;
	}
	
	public function getHooks()
	{
		return $this->hook_list;
	}
	
	public function getAllHooks()
	{
		return self::$hooks;
	}
	
	protected function getAll( $type )
	{
		switch($type)
		{
			case 'actions':
				$hooks = (isset(self::$hooks[$type])) ? self::$hooks[$type] : FALSE;
				break;
			case 'filters':
				$hooks = (isset(self::$hooks[$type])) ? self::$hooks[$type] : FALSE;
				break;
		}
		
		if( $hooks === FALSE )
		{
			$tyh = tyh_get_instance();
			
			if( $tyh->is_debug_on() )
			{
				trigger_error( 'Tyh MVC ' . _x( ucfirst( $type ) . ' have not been set.', TYH_LANG_CONTEXT ), E_USER_WARNING );
			}
			
			return FALSE;
		}
		
		return self::$hooks[$type];
	}
}

class Tyh_Hook_Action extends Tyh_Hook 
{
	
	public function __construct( &$sender )
	{
		parent::__construct( $sender );
		
		$this->hook_type = 'action';
		$this->called = 0;
	}
	
	public static function create($sender)
	{
		return parent::create( $sender, __CLASS__ );
	}
	
	public function add($tag, $callback = '', $priority = 10 , $accepted_args = 1)
	{
		return parent::add($tag, $callback, $priority, $accepted_args, 'action');
	}
	
	public function getActions()
	{
		return parent::getHooks();
	}
	
	public function getAllActions()
	{
		return parent::getAll('actions');
	}
	
	/*
	 * Use one function to register WordPress ajax hooks.
	 */
	public function ajax( $tag, $callback = '', $nopriv = FALSE, $priority = 10 , $accepted_args = 1 )
	{	
		$priv = 'wp_ajax_';
		
		$result = $this->add($priv.$tag, $callback, $priority, $accepted_args );
		
		// call this function only with $nopriv set to false so it won't loop
		if( $nopriv )
		{
			$priv = 'nopriv_';
			// modify tag and then call nopriv action
			$this->ajax( $priv.$tag, $callback, FALSE, $priority, $accepted_args );	
		}
		
		return $result;
	}
}

class Tyh_Hook_Filter extends Tyh_Hook
{	
	public function __construct( &$sender )
	{
		parent::__construct( $sender );
		
		$this->hook_type = 'filter';
	}
	
	public static function create($sender)
	{
		return parent::create( $sender, __CLASS__ );
	}
	
	public function add($tag, $callback = '', $priority = 10 , $accepted_args = 1)
	{
		return parent::add($tag, $callback, $priority, $accepted_args, 'filter');
	}
	
	public function getObjectFilters()
	{
		return parent::getHooks();
	}
	
	public function getAllFilters()
	{
		return parent::getAll('filters');
	}
}